<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>xeokit Example</title>
    <link href="../css/pageStyle.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js"></script>
</head>
<body>
<input type="checkbox" id="info-button"/>
<label for="info-button" class="info-button"><i class="far fa-3x fa-question-circle"></i></label>
<canvas id="myCanvas"></canvas>
<div class="slideout-sidebar">
    <img class="info-icon" src="../../assets/images/performance_model_icon.png"/>
    <h1>Picking SceneModel Surfaces with Full Precision</h1>
    <h2>Hover over objects to ray-pick them using the 'pickSurfacePrecision' option</h2>
    <h3>Components Used</h3>
    <ul>
        <li>
            <a href="../../docs/class/src/viewer/Viewer.js~Viewer.html"
               target="_other">Viewer</a>
        </li>
        <li>
            <a href="../../docs/class/src/viewer/scene/model/SceneModel.js~SceneModel.html"
               target="_other">SceneModel</a>
        </li>
    </ul>
</div>
</body>

<script type="module">

    //------------------------------------------------------------------------------------------------------------------
    // Import the modules we need for this example
    //------------------------------------------------------------------------------------------------------------------

    import {
        Viewer,
        SceneModel,
        Node,
        Mesh,
        VBOGeometry,
        PhongMaterial,
        math,
        buildSphereGeometry
    } from "../../dist/xeokit-sdk.min.es.js";

    //------------------------------------------------------------------------------------------------------------------
    // Create a Viewer and arrange the camera
    //------------------------------------------------------------------------------------------------------------------

    const viewer = new Viewer({
        canvasId: "myCanvas",
        transparent: true,
        pickSurfacePrecisionEnabled: true
    });

    viewer.scene.camera.eye = [-21.80, 4.01, 6.56];
    viewer.scene.camera.look = [0, -5.75, 0];
    viewer.scene.camera.up = [0.37, 0.91, -0.11];

    viewer.cameraControl.followPointer = true;
    viewer.cameraControl.smartPivot = true;

    //------------------------------------------------------------------------------------------------------------------
    // Create a SceneModel representing a table with four legs, using geometry batching
    //------------------------------------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------------------------------------
    // Build a SceneModel representing a table with four legs, using geometry instancing
    //------------------------------------------------------------------------------------------------------------------

    const vboSceneModel = new SceneModel(viewer.scene, {
        id: "table",
        isModel: true, // <--------------------- Represents a model, registers SceneModel by ID on viewer.scene.models
        position: [0, 0, 0],
        scale: [1, 1, 1],
        rotation: [0, 0, 0],
        edges: true
    });

    //--------------------------------------------------------
    // Create a reusable geometry within the SceneModel
    // We'll instance this geometry by five meshes
    //--------------------------------------------------------

    vboSceneModel.createGeometry({

        id: "myBoxGeometry",

        // The primitive type - allowed values are "points", "lines" and "triangles".
        // See the OpenGL/WebGL specification docs for how the coordinate arrays are supposed to be laid out.
        primitive: "triangles",

        // The vertices - eight for our cube, each
        // one spanning three array elements for X,Y and Z
        positions: [
            1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, // v0-v1-v2-v3 front
            1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, // v0-v3-v4-v1 right
            1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, 1, // v0-v1-v6-v1 top
            -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, // v1-v6-v7-v2 left
            -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1,// v7-v4-v3-v2 bottom
            1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1 // v4-v7-v6-v1 back
        ],

        // Normal vectors, one for each vertex
        normals: [
            0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,// v0-v1-v2-v3 front
            1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,// v0-v3-v4-v5 right
            0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,// v0-v5-v6-v1 top
            -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,// v1-v6-v7-v2 left
            0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,// v7-v4-v3-v2 bottom
            0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1// v4-v7-v6-v5 back
        ],

        // Indices - these organise the positions and and normals
        // into geometric primitives in accordance with the "primitive" parameter,
        // in this case a set of three indices for each triangle.
        //
        // Note that each triangle is specified in counter-clockwise winding order.
        //
        indices: [
            0, 1, 2, 0, 2, 3,            // front
            4, 5, 6, 4, 6, 7,            // right
            8, 9, 10, 8, 10, 11,         // top
            12, 13, 14, 12, 14, 15,      // left
            16, 17, 18, 16, 18, 19,      // bottom
            20, 21, 22, 20, 22, 23
        ]
    });

    //--------------------------------------------------------
    // Red table leg object
    //--------------------------------------------------------

    vboSceneModel.createMesh({
        id: "redLegMesh",
        geometryId: "myBoxGeometry",
        position: [-4, -6, -4],
        scale: [1, 3, 1],
        rotation: [0, 0, 0],
        color: [1, 0.3, 0.3]
    });

    vboSceneModel.createEntity({
        id: "redLeg",
        meshIds: ["redLegMesh"],
        isObject: true // <----------------- Represents an object, registers Entity by ID on viewer.scene.objects
    });

    //--------------------------------------------------------
    // Green table leg object
    //--------------------------------------------------------

    vboSceneModel.createMesh({
        id: "greenLegMesh",
        geometryId: "myBoxGeometry",
        position: [4, -6, -4],
        scale: [1, 3, 1],
        rotation: [0, 0, 0],
        color: [0.3, 1.0, 0.3]
    });

    vboSceneModel.createEntity({
        id: "greenLeg",
        meshIds: ["greenLegMesh"],
        isObject: true // <----------------- Represents an object, registers Entity by ID on viewer.scene.objects
    });

    //--------------------------------------------------------
    // Blue table leg
    //--------------------------------------------------------

    vboSceneModel.createMesh({
        id: "blueLegMesh",
        geometryId: "myBoxGeometry",
        position: [4, -6, 4],
        scale: [1, 3, 1],
        rotation: [0, 0, 0],
        color: [0.3, 0.3, 1.0]
    });

    vboSceneModel.createEntity({
        id: "blueLeg",
        meshIds: ["blueLegMesh"],
        isObject: true // <----------------- Represents an object, registers Entity by ID on viewer.scene.objects
    });

    //--------------------------------------------------------
    // Yellow table leg
    //--------------------------------------------------------

    vboSceneModel.createMesh({
        id: "yellowLegMesh",
        geometryId: "myBoxGeometry",
        position: [-4, -6, 4],
        scale: [1, 3, 1],
        rotation: [0, 0, 0],
        color: [1.0, 1.0, 0.0]
    });

    vboSceneModel.createEntity({
        id: "yellowLeg",
        meshIds: ["yellowLegMesh"],
        isObject: true // <----------------- Represents an object, registers Entity by ID on viewer.scene.objects
    });

    //--------------------------------------------------------
    // Purple table top
    //--------------------------------------------------------

    vboSceneModel.createMesh({
        id: "purpleTableTopMesh",
        geometryId: "myBoxGeometry",
        position: [0, -3, 0],
        scale: [6, 0.5, 6],
        rotation: [0, 0, 0],
        color: [1.0, 0.3, 1.0]
    });

    vboSceneModel.createEntity({
        id: "purpleTableTop",
        meshIds: ["purpleTableTopMesh"],
        isObject: true // <----------------- Represents an object, registers Entity by ID on viewer.scene.objects
    });

    //--------------------------------------------------------
    // Finalize this SceneModel.
    //
    // Internally, this builds any geometry batches or instanced
    // arrays that are currently under construction.
    //--------------------------------------------------------

    vboSceneModel.finalize();


    //------------------------------------------------------------------------------------------------------------------
    // Dynamically pick surfaces of model with the pointer, show pick intersection with red sphere
    //------------------------------------------------------------------------------------------------------------------

    //------------------------------------------------------------------------------------------------------------------
    // Display an indicator wherever the pointer picks a surface
    //------------------------------------------------------------------------------------------------------------------

    var hitHelper = new (function () {

        const zeroVec = new Float64Array([0, 0, -1]);
        const quat = new Float64Array(4);

        var node = new Node(viewer.scene, {
            pickable: false,
            visible: true,
            position: [0, 0, 0],

            children: [
                new Mesh(viewer.scene, {
                    geometry: new VBOGeometry(viewer.scene, buildSphereGeometry({radius: .2})),
                    material: new PhongMaterial(viewer.scene, {emissive: [1, 0, 0], diffuse: [0, 0, 0]}),
                    pickable: false
                }),
                new Mesh(viewer.scene, {
                    geometry: new VBOGeometry(viewer.scene, {
                        primitive: "lines",
                        positions: [
                            0.0, 0.0, 0.0, 0.0, 0.0, -2.0
                        ],
                        indices: [0, 1]
                    }),
                    material: new PhongMaterial(viewer.scene, {emissive: [1, 1, 0], diffuse: [0, 0, 0], lineWidth: 4}),
                    pickable: false
                })
            ]
        });

        this.show = function (hit) {
            node.position = hit.worldPos;
            node.visible = true;

            (this._dir = this._dir || math.vec3()).set(hit.worldNormal || [0, 0, 1]);
            math.vec3PairToQuaternion(zeroVec, this._dir, quat);
            node.quaternion = quat;
        };

        this.hide = function () {
            node.visible = false;
        };
    })();

    var lastEntity = null;
    var lastColorize = null;

    viewer.scene.input.on("mousemove", function (coords) {

        var hit = viewer.scene.pick({
            canvasPos: coords,
            pickSurface: true,  // <<------ This causes picking to find the intersection point on the entity
            pickSurfacePrecision: true // <<------ Do precise picking (can be slow - actually not the best for hovering)
        });

        if (hit) {

            if (!lastEntity || hit.entity.id !== lastEntity.id) {

                if (lastEntity) {
                    lastEntity.colorize = lastColorize;
                }

                lastEntity = hit.entity;
                lastColorize = hit.entity.colorize.slice();

                hit.entity.colorize = [0.0, 1.0, 0.0, 1.0];
            }

            hitHelper.show(hit);

        } else {

            if (lastEntity) {
                lastEntity.colorize = lastColorize;
                lastEntity = null;

                hitHelper.hide();
            }
        }
    });
</script>
</html>